package com.yonyou.ucf.mdd.ext.service;

import com.yonyou.cloud.plugin.InvokeRequest;
import com.yonyou.cloud.plugin.InvokeResponse;
import com.yonyou.cloud.plugin.PluginManager;
import com.yonyou.cloud.yts.service.ITransactionService;
import com.yonyou.ucf.mdd.api.interfaces.rpc.IRuleApi;
import com.yonyou.ucf.mdd.common.constant.MddConstants;
import com.yonyou.ucf.mdd.common.dto.BaseReqDto;
import com.yonyou.ucf.mdd.common.enums.OperationTypeEnum;
import com.yonyou.ucf.mdd.common.exceptions.message.MddBpmMsgException;
import com.yonyou.ucf.mdd.common.interfaces.rule.ICacheRpcService;
import com.yonyou.ucf.mdd.common.model.rule.RuleContext;
import com.yonyou.ucf.mdd.common.model.rule.RuleExecuteResult;
import com.yonyou.ucf.mdd.common.model.rule.RuleRegister;
import com.yonyou.ucf.mdd.common.model.uimeta.UIMetaBaseInfo;
import com.yonyou.ucf.mdd.common.model.uimeta.filter.vo.QueryParamVo;
import com.yonyou.ucf.mdd.common.utils.MddBaseUtils;
import com.yonyou.ucf.mdd.common.utils.OperatorLog;
import com.yonyou.ucf.mdd.common.utils.json.GsonHelper;
import com.yonyou.ucf.mdd.core.utils.MetaAttributeUtils;
import com.yonyou.ucf.mdd.ext.bill.consts.BillConstants;
import com.yonyou.ucf.mdd.ext.bill.dto.BaseDto;
import com.yonyou.ucf.mdd.ext.bill.dto.BillDataDto;
import com.yonyou.ucf.mdd.ext.bill.rule.base.AbstractCommonRule;
import com.yonyou.ucf.mdd.ext.bill.rule.base.CommonRuleUtils;
import com.yonyou.ucf.mdd.ext.bill.rule.base.IRule;
import com.yonyou.ucf.mdd.ext.bill.rule.template.CommonOperator;
import com.yonyou.ucf.mdd.ext.consts.Constants;
import com.yonyou.ucf.mdd.ext.core.AppContext;
import com.yonyou.ucf.mdd.ext.dao.meta.LogicDeleteHelper;
import com.yonyou.ucf.mdd.ext.dao.meta.MetaDaoHelper;
import com.yonyou.ucf.mdd.ext.dao.meta.UiMetaDaoHelper;
import com.yonyou.ucf.mdd.ext.dao.sql.SqlHelper;
import com.yonyou.ucf.mdd.ext.exceptions.BusinessException;
import com.yonyou.ucf.mdd.ext.model.BillContext;
import com.yonyou.ucf.mdd.ext.utils.AdapterConvert;
import com.yonyou.ucf.mdd.rule.chain.IRpcRuleAfterPlugin;
import com.yonyou.ucf.mdd.rule.chain.IRpcRuleBeforePlugin;
import com.yonyou.ucf.mdd.rule.chain.IRpcRuleFinallyPlugin;
import com.yonyou.ucf.mdd.rule.utils.RuleUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.imeta.orm.base.BizObject;
import org.imeta.orm.schema.QueryCondition;
import org.imeta.orm.schema.QueryField;
import org.imeta.orm.schema.QuerySchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Title:
 * Description:
 *
 * @author fuxhh
 * @Date: 2019/12/30 11:06
 * @Version 1.0
 */
@Component("mddExtRuleApiService")
public class MddExtRuleApiService implements IRuleApi {
    private static final Logger logger = LoggerFactory.getLogger(MddExtRuleApiService.class);

    @Autowired(required = false)
    ITransactionService transactionService;
    @Autowired
    ICacheRpcService cacheRpcService = new CacheRpcService();
    @Override
    @Transactional
    public <T> RuleExecuteResult executeRule(RuleRegister billRule, RuleContext ruleContext, T... tObjs) throws Exception {

        Map<String, Object> paramMap = ruleContext.getCustomMap();
        cacheRpcService.restore(paramMap);
        BillContext billContext = getBillContext4Compatible(ruleContext);

        // BillContext billCon
        // 由于rpc 传递之前 billContext 中不传递租户， 租户通过ruleContext 传递过来
        Object tenantId = ruleContext.getTenantId();
        tenantId = tenantId == null ? AppContext.getTenantId() : tenantId;
        if (billContext.getTenant() == null) {
            billContext.setTenant(tenantId);
        }
        IRule rule = (IRule) CommonOperator.getRule(billRule);
        if (null != rule) {
            try {
                final BillContext billContextClone = billContext;
                OperatorLog.Callback callback = new OperatorLog.Callback() {
                    @Override
                    public RuleExecuteResult execute() throws Exception {
                        InvokeResponse response = new InvokeResponse();
                        InvokeRequest request = RuleUtil.createInvokeRequest(billRule, billContextClone, paramMap, false, AppContext.getTenantId());
                        request.setAttribute(MddConstants.PLUGIN_KEY_TXSERVICE, transactionService);
                        PluginManager.invoke(IRpcRuleBeforePlugin.class, request, response);

                        RuleExecuteResult pluginResult = (RuleExecuteResult) response.getAttribute(MddConstants.PLUGIN_KEY_RESULT);
                        if (pluginResult != null) {
                            if (shoudThrow(pluginResult)) {
                                logger.error("执行插件出现了异常,{}", pluginResult.getMessage());
                                throw new BusinessException(pluginResult.getMessage());
                            } else {
                                return pluginResult;
                            }
                        }

                        RuleExecuteResult result = null;
                        try {
                            if (rule instanceof AbstractCommonRule) {
                                ((AbstractCommonRule) rule).setConfig(billRule.getConfig());
                            }
                            result = rule.execute(billContextClone, paramMap);

                            PluginManager.invoke(IRpcRuleAfterPlugin.class, request, response);
                            pluginResult = (RuleExecuteResult) response.getAttribute(MddConstants.PLUGIN_KEY_RESULT);
                            if (pluginResult != null) {
                                if (shoudThrow(pluginResult)) {
                                    logger.error("执行插件出现了异常,{}", pluginResult.getMessage());
                                    throw new BusinessException(pluginResult.getMessage());
                                } else {
                                    return pluginResult;
                                }
                            }
                        } catch (Exception e) {
                            throw e;
                        } finally {

                        }

                        //出参
                        BaseDto dto = (BaseDto) paramMap.get(MddConstants.PARAM_PARAM);
                        if (null != dto) {
                            Object obj = dto.getData();
                            if (null != obj && obj instanceof List) {
                                List list = (List) obj;
                                list = CommonRuleUtils.clearParent(list);
                                dto.setData(list);
                            }
                        }
                        Map<String, Object> outParams = new HashMap<>();
                        billContextClone.setTenant(null);
                        billContextClone.setTenantId(null);
                        outParams.put("billContext", billContextClone);
                        outParams.put("paramMap", paramMap);
                        if (result == null) {
                            result = new RuleExecuteResult();
                        }
                        result.setOutParams(outParams);
                        response.setAttribute(MddConstants.PLUGIN_KEY_RESULT, result);
                        PluginManager.invoke(IRpcRuleFinallyPlugin.class, request, response);
                        cacheRpcService.afterInvoke(paramMap);
                        return result;
                    }
                };
                return OperatorLog.traceLog(billRule, billContext, paramMap, callback);
            } catch (Throwable e) {
                logger.error(">>MddExtRuleApiService##executeRule##debug", e);
                throw new BusinessException(e.getMessage(), e);
            } finally {
                try {
                    List<String> needRollList = (List<String>) AppContext.getCaptureAttribute("mddUnionColumnKey");
                    if (null != needRollList && needRollList.size() > 0) {
                        AppContext.cache().del(needRollList.toArray(new String[needRollList.size()]));
                    }
                } catch (Exception e) {
                    logger.error("清理缓存KEY 失败：{}", e.getMessage(), e);
                }
            }
        }
        return new RuleExecuteResult();

    }

    private boolean shoudThrow(RuleExecuteResult pluginResult) {
        return pluginResult.getCode() == -1;
    }

    @Override
    @Transactional
    public <T> RuleExecuteResult executeRule(RuleContext ruleContext) throws Exception {
        Map<String, Object> paramMap = ruleContext.getCustomMap();
        BillContext billContext = getBillContext4Compatible(ruleContext);

        String action = billContext.getAction();
        if (null == billContext && "".equals(action)) {
            action = ruleContext.getAction();
        }
        RuleExecuteResult ruleExecuteResult = new CommonOperator(action).executeRule(billContext, paramMap, "common");
        return ruleExecuteResult;
    }

    @Override
    @Transactional
    public RuleExecuteResult pureExecuteRule(RuleContext ruleContext, BaseReqDto bill) throws Exception {
        BillContext billContext = getBillContext4Compatible(ruleContext);
        BaseDto newBaseDto = AdapterConvert.convert2BillDataDto(bill);
        if(OperationTypeEnum.UNAUDIT.getValue().equals(billContext.getAction())
                && StringUtils.isNotBlank((String)billContext.getCusMapValue("processStartAndAutoEndWithdraw"))
                && StringUtils.isNotBlank((String)billContext.getCusMapValue("businessKey"))){
            // 撤回审批一根筋的特殊情况
            RuleExecuteResult result =  executeRule(billContext, newBaseDto);

            fillDataByQuery(newBaseDto, billContext );

            billContext.setAction(OperationTypeEnum.UNSUBMIT.getValue());
            RuleExecuteResult result2 =  executeRule(billContext, newBaseDto);
            logger.warn("流程一根筋撤回调用 UNSUBMIT 返回结果：{}",result2);
            return result;
        } else {
            return executeRule(billContext, newBaseDto);
        }
    }

    public void fillDataByQuery(BaseDto newBaseDto,BillContext billContext) throws Exception {
        String businessKey = (String) billContext.getCusMapValue("businessKey");

        QuerySchema qSchema = new QuerySchema();
        qSchema.addSelect(new QueryField(MddConstants.PARAM_ID, MddConstants.PARAM_ID));
        qSchema.addSelect(new QueryField(MddConstants.PARAM_PUB_TS, MddConstants.PARAM_PUB_TS));
        qSchema.addSelect(new QueryField(Constants.CONFIG_BILL_STATUS_FILED, Constants.CONFIG_BILL_STATUS_FILED));
        qSchema.addSelect(new QueryField(MddConstants.PARAM_BPM_VERIFYSTATE, MddConstants.PARAM_BPM_VERIFYSTATE));
        qSchema.addSelect(new QueryField(MddConstants.PARAM_BPM_IS_WF_CONTROLLED, MddConstants.PARAM_BPM_IS_WF_CONTROLLED));
        String orgField = MetaAttributeUtils.getFieldByAttributeOrName(billContext.getFullname(), "isMasterOrg", "org");
        qSchema.addSelect(new QueryField(orgField, orgField));
        qSchema.appendQueryCondition(QueryCondition.name(MddConstants.PARAM_ID).eq(MddBaseUtils.getIdFromBusinessKey(businessKey)));
        List<Map<String,Object>> queryList = MetaDaoHelper.query(billContext, qSchema);
        if(CollectionUtils.isNotEmpty(queryList) && queryList.size() == 1){
            Map<String, Object> json = queryList.get(0);
            newBaseDto.setData(GsonHelper.ToJSon(json,"yyyy-MM-dd HH:mm:ss"));
        } else {
            logger.error("一根筋撤回，调用 UNSUBMIT 前查询数据结果不正确，{}", queryList);
            throw new MddBpmMsgException("一根筋撤回，调用 UNSUBMIT 前查询数据结果不正确");
        }
    }


    @Override
    public List<Map<String, Object>> getMakeBillRuleList(String targetType) throws Exception {
        if (!StringUtils.isEmpty(targetType)) {
            QuerySchema querySchema = QuerySchema.create().addSelect("*").
                    appendQueryCondition(QueryCondition.name("target_type").eq(targetType));
            List<Map<String, Object>> targetList = MetaDaoHelper.query(BillConstants.RULEENTITY, querySchema);
            return targetList;
        }
        return null;
    }

    @Override
    public List<Map<String, Object>> getRuleBackSourceList(List<String> makeBillCodes) throws Exception {
        if (!CollectionUtils.isEmpty(makeBillCodes)) {
            List<Map<String, Object>> ruleList = SqlHelper.selectList("com.yonyou.ucf.mdd.ext.makeBillRuleMapper.getRuleBackSourceList", makeBillCodes);
            return ruleList;
        }
        return null;
    }

    @Override
    public RuleExecuteResult doAction(String action, RuleRegister ruleRegister, RuleContext ruleContext) throws
            Exception {
        Map<String, Object> params = ruleContext.getCustomMap();
        BillContext billContext = getBillContext4Compatible(ruleContext);
        return new CommonOperator().doAction(action, ruleRegister, billContext, params);
    }


    @Override
    @Transactional
    public RuleExecuteResult executeRule(BillContext billContext, BaseDto bill) throws Exception {
        try {
            RuleExecuteResult ruleExecuteResult = new CommonOperator(billContext.getAction()).executeRule(billContext, bill);
            return ruleExecuteResult;
        } catch (Exception e) {
            logger.error("MddExtRuleApiService.executeRule error", e);
            if(e instanceof BusinessException){
                throw e;
            } else {
                throw new BusinessException(e.getMessage(),e);
            }
        }
    }

    /**
     * @Description 兼容处理 ext 3.0.2 -> 3.0.4
     * @Param ruleContext
     * @return com.yonyou.ucf.mdd.ext.model.BillContext
     * @Author DaeDalu$
     * @Date
     **/
    private BillContext getBillContext4Compatible(RuleContext ruleContext) {
        BillContext billContext = ruleContext.getBillContext();  //3.0.4 -> 3.0.4
        /*logger.info("com.yonyou.ucf.mdd.ext.service.MddExtRuleApiService.getBillContext4Compatible --> ruleContext.getBillContext(){}", billContext);
        if (billContext == null && ruleContext instanceof BillContext) {//ext 3.0.2 -> 3.0.4
            ruleContext.setCustomMap(null);
            billContext = (BillContext) ruleContext;
            logger.info("com.yonyou.ucf.mdd.ext.service.MddExtRuleApiService.getBillContext4Compatible --> ruleContext case to billContext{}", billContext);
        }

        if (billContext == null) {//mdd 3.0.2 -> 3.0.4
            UIMetaBaseInfo uiMetaBaseInfo = ruleContext.getUimetaBaseInfoCompatible();
            if (null != uiMetaBaseInfo) {
                billContext = AdapterConvert.convert2BillContext(uiMetaBaseInfo);
                logger.info("com.yonyou.ucf.mdd.ext.service.MddExtRuleApiService.getBillContext4Compatible --> UIMetaBaseInfo convert to billContext{}", billContext);
            }
        }*/
        return billContext;
    }
}
